package framework.transport.codec;

import framework.enums.CompressTypeEnum;
import framework.enums.SerializationTypeEnum;
import framework.extension.ExtensionLoader;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;

@Slf4j
public class XMessageCodec extends MessageToMessageCodec<ByteBuf, XMessage> {

    @Override
    protected void encode(ChannelHandlerContext ctx, XMessage xMessage, List<Object> list) throws Exception {

        try {
            ByteBuf buf = ctx.alloc().buffer();
            buf.writeBytes(xMessage.magic_num);         // 写入魔数 自定义协议的标识
            buf.writeByte(xMessage.protocol_version);   // 写入协议版本号
            int dataLengthIndex = buf.writerIndex();
            buf.writerIndex(buf.writerIndex() + 4);   // 预留4个字节的长度用于存储描述数据长度的空间
            buf.writeByte(xMessage.getMessageType());       // 写入消息类型
            buf.writeByte(xMessage.getCodec());             // 写入序列化类型
            buf.writeByte(xMessage.getCompress());          // 写入压缩算法
            // 开始序列化数据包
            String name = SerializationTypeEnum.getName(xMessage.getCodec());
            byte[] bodyContent = ExtensionLoader.getSerialize(name).serialize(xMessage.getData());
            // 开始压缩数据包 0x00 意味着 不进行数据压缩
            if (xMessage.getCompress() != 0) {
                String compressName = CompressTypeEnum.getName(xMessage.getCompress());
                bodyContent = ExtensionLoader.getCompress(compressName).compress(bodyContent);
            }
            int fullLength = xMessage.protocol_body_head_length + bodyContent.length;
            buf.writeBytes(bodyContent);
            int writeIndex = buf.writerIndex();
            buf.writerIndex(dataLengthIndex);   // 写指针移动到预留的数据长度的位置
            buf.writeInt(fullLength);           // 写入数据长度
            buf.writerIndex(writeIndex);           // 写指针移动到末尾
            list.add(buf);
        } catch (Exception ex) {
            log.error("数据编码失败 requestId:{} message:{}", xMessage.getRequestId(), xMessage.getMessageType());
            ex.printStackTrace();
            throw ex;
        }
    }
    public static String bytesToHexString(byte[] src){
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        for (byte b : src) {
            int v = b & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv).append(" ");
        }
        return stringBuilder.substring(0, stringBuilder.toString().length() - 1);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) throws Exception {

        XMessage xMessage = new XMessage();
        checkMagicNumber(xMessage, byteBuf);
        checkProtocolVersion(xMessage, byteBuf);

        int dataLength = byteBuf.readInt();
        byte messageType = byteBuf.readByte();
        byte codec = byteBuf.readByte();
        byte compress = byteBuf.readByte();

        xMessage.setMessageType(messageType);
        xMessage.setCodec(codec);
        xMessage.setCompress(compress);

        int bodyLength = dataLength - xMessage.protocol_body_head_length;

        if (bodyLength > 0) {
            byte[] bodyContent = new byte[bodyLength];
            byteBuf.readBytes(bodyContent);

            // 解压数据
            if (compress != 0x00) {
                bodyContent = ExtensionLoader.getCompress(CompressTypeEnum.getName(compress)).decompress(bodyContent);
            }
            // 反序列化数据
            if (messageType == 0x01) {
                XRequest xRequest = ExtensionLoader.getSerialize(SerializationTypeEnum.getName(codec)).deserialize(bodyContent, XRequest.class);
                xMessage.setData(xRequest);
            } else {
                XResponse xResponse = ExtensionLoader.getSerialize(SerializationTypeEnum.getName(codec)).deserialize(bodyContent, XResponse.class);
                xMessage.setData(xResponse);
            }
        }
        list.add(xMessage);
    }

    private void checkProtocolVersion(XMessage xMessage, ByteBuf byteBuf) {
        byte version = byteBuf.readByte();
        if (version != xMessage.protocol_version) {
            throw new RuntimeException("version isn't compatible" + version);
        }
    }

    private void checkMagicNumber(XProtocolV1 xProtocolV1, ByteBuf byteBuf) {

        int len = xProtocolV1.magic_num.length;
        byte[] bytes = new byte[len];
        byteBuf.readBytes(bytes);
        for (int i = 0; i < len; i++) {
            if (bytes[i] != xProtocolV1.magic_num[i]) {
                throw new IllegalArgumentException("Unknown magic code: " + Arrays.toString(bytes));
            }
        }
    }
}
